import logging
import os
import glob
import numpy as np
from FCM.ClusterResult import ClusterResult
import csv
import time
from concurrent.futures import ThreadPoolExecutor

from FCM.SPDTW2 import SpDtw2
from FCM.dtwfast import dtwfast
from FCM.maxrowf import MaxRowF

logging.basicConfig(level=logging.INFO,
                    format="%(asctime)s [%(thread)d] %(levelname)s %(message)s")
logger = logging.getLogger()


def import_excel_matrix(path):
    with open(path, encoding='UTF-8') as f:
        sheet = list(csv.reader(f))

    table = np.array(sheet)
    # 第一个数字乱码
    table[0][0] = table[1][0]
    row = table.shape[0]  # 行数
    col = table.shape[1]  # 列数
    logger.info("row %s col %s" % (row, col))
    data_matrix = np.zeros((row, col - 1))  # 生成一个nrows行*ncols-1列的初始矩阵
    data_type = np.zeros(row)
    for i in range(row):  # 对行进行遍历
        data_type[i] = table[i, 0]
        for j in range(1, col):
            data_matrix[i, j - 1] = table[i][j]  # 按列把数据存进矩阵中
    return data_matrix, data_type


def transToMatrix(array):
    matrix = np.zeros([1, len(array)])
    for i in range(len(array)):
        matrix[0, i] = array[i]
    return matrix


def sumMatrix(matrix):
    N, S = matrix.shape
    sumCount = 0.0
    for i in range(N):
        for j in range(S):
            sumCount += matrix[i][j]
    return sumCount


def fillMatrix(U, v, row):
    for i in range(len(v)):
        U[row, i] = v[i]
    return U


def intersection(nums1, nums2):
    nums1 = list(set(nums1))  # 去重
    nums1.sort()  # 升序排列
    nums2 = list(set(nums2))
    nums2.sort()
    i = 0  # 数组1指针
    j = 0  # 数组2指针
    res = []  # 结果数组
    while i < len(nums1) and j < len(nums2):  # 循环条件
        if nums1[i] == nums2[j]:  # 两个值想等得情况
            res.append(nums1[i])  # 将值加入结果数组
            i += 1  # 数组1指针后移
            j += 1  # 数组2指针后移
        elif nums1[i] < nums2[j]:  # 数组1得值小于数组2
            i += 1  # 数组1得指针后移
        elif nums1[i] > nums2[j]:  # 数组1得值大于数组2
            j += 1  # 数组2得指针后移
    return res  # 返回结果数组


def FcmCluster(data, data_type, a):  # a为加权系数
    logger.info("data %s" % data)
    # 获取聚类数
    C = len(set(data_type))
    logger.info("clusterNum %s" % C)
    M = 2
    N, S = data.shape
    logger.info("row = %s col = %s" % (N, S))

    m = 2 / (M - 1)
    Iter = 0
    Dist = np.zeros([C, N])
    U = np.zeros([C, N])
    P0 = np.random.rand(C, S)
    # Obj_Fcn = []

    # FCM的迭代算法
    while True:
        # 迭代计数器
        Iter = Iter + 1
        # 计算或更新划分矩阵U
        dist1 = np.zeros([C, N])
        dist2 = np.zeros([C, N])

        for i in range(C):
            for j in range(N):
                dist1[i, j] = dtwfast(P0[i], data[j])

        for i in range(C):
            for j in range(N):
                dist2[i, j] = SpDtw2(P0[i], data[j])

        for i in range(C):
            for j in range(N):
                Dist[i, j] = (1 - a) * dist1[i, j] + a * dist2[i, j]

        # np.dot(a,b)矩阵乘
        # w*x 点乘， Um'转置矩阵
        U = np.power(np.power(Dist, m) * np.dot(np.ones([C, 1]), transToMatrix(np.sum(np.power(Dist, -m), axis=0))), -1)

        # 更新聚类中心P
        # matlab中 ./和.* 代表矩阵对应元素相除 和相乘，俗称点除和点乘。而在python中，/和*代表 点除和点乘。与matlab 不相同。
        # matlab中/和*代表 交叉除和交叉乘。在python中需要使用numpy的dot()函数.python中只有/ 点除。交叉除等价于 乘以矩阵的逆矩阵。
        Um = np.power(U, M)
        P = Um.dot(data) / np.transpose((np.ones([S, 1])).dot(np.sum(np.transpose(Um))))

        # 目标函数值: 类内加权平方误差和
        # Obj_Fcn.append(sumMatrix(Um * np.power(Dist, 2)))
        # FCM算法迭代停止条件
        # norm(A, inf):返回max(abs(A))
        pauRes = np.max(abs(P - P0))
        logger.info("pauRes = %s; Iter = %s" % (pauRes, Iter))
        if pauRes < 1.0e-2 or Iter > 5:
            break
        P0 = P

    # 聚类结果
    res = MaxRowF(U)
    # 求得矩阵Cj
    Cj = np.zeros([C, len(res)])
    for cc in range(C):
        v = np.where(res == cc)
        Cj = fillMatrix(Cj, v[0], cc)

    # 求得矩阵Ki
    nn = len(data_type)
    Ki = np.zeros([C, nn])
    for k in range(C):
        v = np.where(data_type == k + 1)
        Ki = fillMatrix(Ki, v[0], k)

    # 求得Ki与Cj并的个数
    KC = np.zeros([C, C])
    for i in range(C):
        for j in range(C):
            KC[i, j] = len(intersection(Ki[i], Cj[j]))

    z = np.zeros(C)
    for j in range(C):
        z[j] = (max(KC[:, j])) / N

    return sum(z)


def loadDataParallel():
    workspace = os.path.dirname(__file__)
    resultList = []
    # 加权指数列表
    aLst = [round(num, 1) for num in np.arange(0, 1.1, 0.2)]
    # 文件路径列表
    fileLst = []
    for data_file in glob.glob(os.path.join(workspace, "datasource", "Car.csv")):
        fileLst.append(data_file)

    threadPool = ThreadPoolExecutor(max_workers=6, thread_name_prefix="test_")
    i = 0
    for [data_matrix, data_type] in threadPool.map(import_excel_matrix, fileLst):
        data_file = fileLst[i]
        i += 1
        # 截取文件名
        file_name = data_file.split("\\")[-1]
        logger.info("data_file %s" % file_name)
        resultMap = {}
        for a in aLst:
            result = FcmCluster(data_matrix, data_type, a)
            logger.info("a %s data %s" % (a, result))
            resultMap.setdefault(a, result)

        cluResult = ClusterResult(file_name, resultMap)
        resultList.append(cluResult)
    # 关闭线程池
    threadPool.shutdown(wait=True)
    return resultList


def getCurrentTime():
    return int(round(time.time() * 1000))


def LstTransToStr(Lst):
    res = ''
    for i in range(len(Lst)):
        res += str(Lst[i])
        res += ';'
    return res[0:len(res) - 1]


def main():
    try:
        startMillis = getCurrentTime()
        startTime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(startMillis / 1000))
        logger.info("startTime: %s" % startTime)
        result_list = loadDataParallel()
        logger.info("cost time: %s ms" % (getCurrentTime() - startMillis))
        # 將結果集转换为str
        resStr = LstTransToStr(result_list)
        logger.info("result: %s " % resStr)
    except Exception as ex:
        print('Exception:\r\n')
        print(ex)
    finally:
        os.system("pause")


if __name__ == '__main__':
    main()
